/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.core; import java.awt.event.KeyEvent; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.*; import javax.swing.Action; import javax.swing.KeyStroke; import javax.swing.event.*; import javax.swing.text.Keymap; import org.openide.TopManager; import org.openide.filesystems.*; import org.openide.loaders.XMLDataObject; import org.openide.util.Utilities; import org.openide.util.actions.SystemAction; import org.openide.util.HelpCtx; /** * * @author Ian Formanek */ public class ShortcutsEditor extends javax.swing.JPanel { private static final java.util.ResourceBundle bundle = org.openide.util.NbBundle.getBundle(ShortcutsEditor.class); public static final String PROPERTIES_FILE = "Shortcuts"; // NOI18N public static final String DEFAULT_KEYS_FILE = "Default"; // NOI18N public static final String USER_KEYS_FILE = "UserDefined"; // NOI18N private static final String SHORTCUTS_FOLDER = "Shortcuts"; // NOI18N private static final String KEYS_EXT = "keys"; // NOI18N private static final String XML_SHORTCUTS = "Shortcuts"; // NOI18N private static final String XML_DESCRIPTION = "Description"; // NOI18N private static final String XML_BINDING = "Binding"; // NOI18N private static final String ATTR_DESCRIPTION_NAME = "name"; // NOI18N private static final String ATTR_BINDING_KEY = "key"; // NOI18N private static final String ATTR_BINDING_ACTION = "action"; // NOI18N private static final String PROP_CURRENT_KEYFILE = "current"; // NOI18N private ShortcutsPanel shortcutsPanel; private ActionsPanel actionsPanel; private java.awt.Component currentPanel; private boolean modified = false; /** Creates new form ShortcutsEditor */ public ShortcutsEditor() { actionsPanel = new ActionsPanel (true, this); shortcutsPanel = new ShortcutsPanel (this); initComponents (); radioPanel.setBorder (new javax.swing.border.TitledBorder ( new javax.swing.border.EtchedBorder (), bundle.getString ("ShortcutsEditor.typeTitle"))); javax.swing.ButtonGroup group = new javax.swing.ButtonGroup (); group.add(actionsButton); group.add(shortcutsButton); actionsButton.setSelected (true); updateDynamicContent (); requestFocus (); radioPanel.requestFocus(); actionsButton.requestFocus(); HelpCtx.setHelpIDString (this, ShortcutsEditor.class.getName ()); } public java.awt.Dimension getPreferredSize() { return new java.awt.Dimension (370, 600); } void setModified (boolean value) { modified = value; } public boolean isModified () { return modified; } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the FormEditor. */ private void initComponents () {//GEN-BEGIN:initComponents radioPanel = new javax.swing.JPanel (); actionsButton = new javax.swing.JRadioButton (); shortcutsButton = new javax.swing.JRadioButton (); setLayout (new java.awt.BorderLayout ()); setBorder (new javax.swing.border.EmptyBorder(new java.awt.Insets(8, 8, 8, 8))); radioPanel.setLayout (new java.awt.GridBagLayout ()); java.awt.GridBagConstraints gridBagConstraints1; actionsButton.setContentAreaFilled (actionsButton.isContentAreaFilled ()); actionsButton.setText (org.openide.util.NbBundle.getBundle(ShortcutsEditor.class).getString("ShortcutsEditor.actionsButton.text")); actionsButton.addActionListener (new java.awt.event.ActionListener () { public void actionPerformed (java.awt.event.ActionEvent evt) { actionsButtonActionPerformed (evt); } } ); gridBagConstraints1 = new java.awt.GridBagConstraints (); gridBagConstraints1.gridwidth = 0; gridBagConstraints1.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints1.insets = new java.awt.Insets (0, 8, 0, 8); gridBagConstraints1.weightx = 1.0; radioPanel.add (actionsButton, gridBagConstraints1); shortcutsButton.setText (org.openide.util.NbBundle.getBundle(ShortcutsEditor.class).getString("ShortcutsEditor.shortcutsButton.text")); shortcutsButton.addActionListener (new java.awt.event.ActionListener () { public void actionPerformed (java.awt.event.ActionEvent evt) { shortcutsButtonActionPerformed (evt); } } ); gridBagConstraints1 = new java.awt.GridBagConstraints (); gridBagConstraints1.gridwidth = 0; gridBagConstraints1.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints1.insets = new java.awt.Insets (0, 8, 0, 8); gridBagConstraints1.weightx = 1.0; radioPanel.add (shortcutsButton, gridBagConstraints1); add (radioPanel, java.awt.BorderLayout.NORTH); }//GEN-END:initComponents private void shortcutsButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_shortcutsButtonActionPerformed updateDynamicContent (); }//GEN-LAST:event_shortcutsButtonActionPerformed private void actionsButtonActionPerformed (java.awt.event.ActionEvent evt) {//GEN-FIRST:event_actionsButtonActionPerformed updateDynamicContent (); }//GEN-LAST:event_actionsButtonActionPerformed private void updateDynamicContent () { if (actionsButton.isSelected()) { if (currentPanel != actionsPanel) { if (currentPanel != null) remove (currentPanel); currentPanel = actionsPanel; actionsPanel.updateTree (); add (currentPanel, java.awt.BorderLayout.CENTER); revalidate (); repaint (); } } else { if (currentPanel != shortcutsPanel) { if (currentPanel != null) remove (currentPanel); currentPanel = shortcutsPanel; add (currentPanel, java.awt.BorderLayout.CENTER); revalidate (); repaint (); } } } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel radioPanel; private javax.swing.JRadioButton actionsButton; private javax.swing.JRadioButton shortcutsButton; // End of variables declaration//GEN-END:variables // ----------------------------------------------------------------------------- // Static methods static String getKeyStrokeName (KeyStroke stroke) { Keymap map = TopManager.getDefault ().getGlobalKeymap (); Action action = map.getAction (stroke); if (action instanceof SystemAction) { String name = ((SystemAction)action).getName (); name = Utilities.replaceString (name, "&", ""); // remove mnemonics marker // NOI18N name = Utilities.replaceString (name, "...", ""); // remove trailing "..." // NOI18N return getKeyText (stroke) + " [" + name + "]"; // NOI18N } else { if (action == null) return getKeyText (stroke); else return getKeyText (stroke) + " [" + action.getValue (Action.NAME) + "]"; // NOI18N } } static String getActionName (SystemAction action) { String name = Utilities.replaceString (action.getName (), "&", ""); // remove mnemonics marker // NOI18N name = Utilities.replaceString (name, "...", ""); // remove trailing "..." // NOI18N Keymap map = TopManager.getDefault ().getGlobalKeymap (); KeyStroke[] strokes = map.getKeyStrokesForAction(action); if (strokes.length > 0) { name = name + " ["; // NOI18N for (int i = 0; i < strokes.length; i++) { name = name + getKeyText (strokes[i]); if (i != strokes.length - 1) { name = name + ", "; // NOI18N } } return name + "]"; // NOI18N } else { return name; } } static String getKeyText (int keyCode, int modifiers) { String modifText = java.awt.event.KeyEvent.getKeyModifiersText(modifiers); if ("".equals (modifText)) return java.awt.event.KeyEvent.getKeyText(keyCode); // NOI18N else { if ((keyCode == KeyEvent.VK_ALT) || (keyCode == KeyEvent.VK_ALT_GRAPH) || (keyCode == KeyEvent.VK_CONTROL) || (keyCode == KeyEvent.VK_SHIFT)) { return modifText + "+"; // in this case the keyCode text is also among the modifiers // NOI18N } else { return modifText + "+" + java.awt.event.KeyEvent.getKeyText(keyCode); // NOI18N } } } static String getKeyText (KeyStroke stroke) { String modifText = java.awt.event.KeyEvent.getKeyModifiersText(stroke.getModifiers ()); if ("".equals (modifText)) return java.awt.event.KeyEvent.getKeyText(stroke.getKeyCode ()); // NOI18N else return modifText + "+" + java.awt.event.KeyEvent.getKeyText(stroke.getKeyCode ()); // NOI18N } public static void main (String[] args) { javax.swing.JFrame fr = new javax.swing.JFrame (); fr.getContentPane().setLayout(new java.awt.BorderLayout ()); fr.getContentPane().add (new ShortcutsEditor (), java.awt.BorderLayout.CENTER); fr.show (); } static org.w3c.dom.Document parseKeysFile (java.net.URL url) throws org.xml.sax.SAXException, java.io.IOException { return XMLDataObject.parse(url, new org.xml.sax.ErrorHandler () { public void error (org.xml.sax.SAXParseException e) { // [PENDING] } public void warning (org.xml.sax.SAXParseException e) { // [PENDING] } public void fatalError (org.xml.sax.SAXParseException e) { // [PENDING] } } ); } // ----------------------------------------------------------------------------- // public static methods public static void saveCustomKeys () throws java.io.IOException { Keymap map = TopManager.getDefault ().getGlobalKeymap (); KeyStroke[] strokesArray = map.getBoundKeyStrokes(); String indent = " "; // NOI18N org.openide.filesystems.FileSystem systemFS = TopManager.getDefault().getRepository().getDefaultFileSystem (); boolean defaultsUsed = false; HashMap moduleKeyFiles = new HashMap (11); FileObject shortcutsFolder = null; try { shortcutsFolder = systemFS.find (SHORTCUTS_FOLDER, null, null); if (shortcutsFolder == null) { shortcutsFolder = systemFS.getRoot ().createFolder (SHORTCUTS_FOLDER); } } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N } if (shortcutsFolder == null) return; // cannot save [PENDING - notify] FileLock lock = null; try { FileObject mainFO = systemFS.find (SHORTCUTS_FOLDER, USER_KEYS_FILE, KEYS_EXT); if (mainFO == null) { mainFO = shortcutsFolder.createData (USER_KEYS_FILE, KEYS_EXT); } java.io.PrintWriter pw = new java.io.PrintWriter (mainFO.getOutputStream (lock = mainFO.lock ())); pw.println ("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>"); // NOI18N pw.println (); pw.println ("<" + XML_SHORTCUTS + ">"); // NOI18N pw.println (indent + "<" + XML_DESCRIPTION + " " + ATTR_DESCRIPTION_NAME + "=" + "\"User Defined Shortcuts\" />"); // NOI18N pw.println (); for (int i = 0; i < strokesArray.length; i++) { String stroke = Utilities.keyToString (strokesArray[i]); String action = map.getAction (strokesArray[i]).getClass ().getName (); pw.println (indent + "<" + XML_BINDING + " " + ATTR_BINDING_KEY + "=\"" + stroke + "\"" + " " + ATTR_BINDING_ACTION +"=\"" + action + "\" />"); // NOI18N } pw.println ("</" + XML_SHORTCUTS + ">"); // NOI18N pw.close (); } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N } finally { if (lock != null) { lock.releaseLock (); } } FileObject propsFO = systemFS.find (SHORTCUTS_FOLDER, PROPERTIES_FILE, "properties"); // NOI18N FileLock propsLock = null; try { if (propsFO == null) { propsFO = shortcutsFolder.createData (PROPERTIES_FILE, "properties"); // NOI18N } propsLock = propsFO.lock (); Properties props = new Properties (); FileObject[] files = shortcutsFolder.getChildren (); for (int i = 0; i < files.length; i++) { String fName = files[i].getName (); if (files[i].hasExt (KEYS_EXT) && !fName.equals(DEFAULT_KEYS_FILE) && !fName.equals(USER_KEYS_FILE)) { props.put (fName, "Yes"); // NOI18N } } props.store (propsFO.getOutputStream (propsLock), ""); // NOI18N } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N } finally { if (propsLock != null) { propsLock.releaseLock (); } } } public static void installCurrentBindings () { try { org.openide.filesystems.FileSystem systemFS = TopManager.getDefault().getRepository().getDefaultFileSystem (); boolean defaultsUsed = false; HashMap moduleKeyFiles = new HashMap (11); FileObject mainFO = systemFS.find (SHORTCUTS_FOLDER, USER_KEYS_FILE, KEYS_EXT); if (mainFO == null) { defaultsUsed = true; // if no user-defined shortcuts found, try to use the default ones mainFO = systemFS.find (SHORTCUTS_FOLDER, DEFAULT_KEYS_FILE, KEYS_EXT); } // find all .keys files in the Shortcuts folder, FileObject shortcutsFolder = FileUtil.createFolder (systemFS.getRoot (), SHORTCUTS_FOLDER); FileObject[] files = shortcutsFolder.getChildren (); for (int i = 0; i < files.length; i++) { String fName = files[i].getName (); if (files[i].hasExt (KEYS_EXT) && !fName.equals(DEFAULT_KEYS_FILE) && !fName.equals(USER_KEYS_FILE)) { moduleKeyFiles.put (fName, files[i]); } } if (!defaultsUsed) { // // in the case, when the user customized the shortcuts, // we have to deal with the situation, that there might be modules // installed after the time the user performed the customization // and we now have to install their bindings as well // // the PROPERTIES_FILE file contains a list of all module keys files // existing at the point of customization, i.e. their shortcuts are now contained // in the USER_KEYS_FILE and should not be processed // FileObject propsFO = systemFS.find (SHORTCUTS_FOLDER, PROPERTIES_FILE, "properties"); // NOI18N if (propsFO != null) { Properties props = new Properties (); try { props.load (propsFO.getInputStream ()); } catch (IOException e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N } // remove all files with name specified in the properties file from the list of files to process (moduleKeyFiles) for (Enumeration items = props.keys(); items.hasMoreElements (); ) { moduleKeyFiles.remove(items.nextElement()); } } } HashMap allBindings = processKeysFiles (mainFO, moduleKeyFiles.values ()); installBindings (allBindings); } catch (Exception e2) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e2.printStackTrace(); // NOI18N } } public static void installBindings (HashMap strokesMap) { Keymap map = TopManager.getDefault ().getGlobalKeymap (); map.removeBindings(); if (map instanceof NbKeymap) { ((NbKeymap)map).addActionForKeyStrokeMap(strokesMap); } else { // in the case we are working with unknown Keymap implementation, // we have to add the items one by one for (Iterator it = strokesMap.keySet().iterator(); it.hasNext (); ) { KeyStroke key = (KeyStroke)it.next (); map.addActionForKeyStroke(key, (Action) strokesMap.get (key)); } } } /** Processes all files with shortcuts bindings and returns HashMap with all <KeyStroke, Action> mappings. * @param mainKeysFile the main keys file with default bindings, can be null if there is no such file * @param moduleKeyFiles collection of FileObjects with additional shortcuts * @return HashMap of <KeyStroke, Action> mapping of all shortcut bindings */ private static HashMap processKeysFiles (FileObject mainKeysFile, Collection moduleKeysFiles) { HashMap bindings = new HashMap (79); if (mainKeysFile != null) { try { addBindings (parseKeysFile (mainKeysFile.getURL ()), bindings); } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N } } for (Iterator it = moduleKeysFiles.iterator (); it.hasNext (); ) { try { FileObject fo = (FileObject)it.next (); addBindings (parseKeysFile (fo.getURL ()), bindings); } catch (Exception e) { if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N } } return bindings; } private static void addBindings (org.w3c.dom.Document doc, HashMap map) { org.w3c.dom.NodeList nl = doc.getElementsByTagName (XML_BINDING); for (int i = 0; i < nl.getLength (); i++) { try { String key = nl.item (i).getAttributes ().getNamedItem (ATTR_BINDING_KEY).getNodeValue (); String act = nl.item (i).getAttributes ().getNamedItem (ATTR_BINDING_ACTION).getNodeValue (); KeyStroke stroke = Utilities.stringToKey (key); act = org.openide.util.Utilities.translate(act); SystemAction action = SystemAction.get (Class.forName(act, true, TopManager.getDefault ().systemClassLoader ())); map.put (stroke, action); } catch (Exception e) { // NullPointer might be thrown if there is not the correct attribute present if (Boolean.getBoolean ("netbeans.debug.exceptions")) e.printStackTrace(); // NOI18N } } } } /* * Log * 19 Gandalf-post-FCS1.17.1.0 4/6/00 Ian Formanek Fixed bug 6104 - Can not * change Editor Shortcut key Ctrl+A. * 18 Gandalf 1.17 1/16/00 Jesse Glick Context help. * 17 Gandalf 1.16 1/15/00 Jaroslav Tulach Should survive non * existing Shortcuts folder and create it. * 16 Gandalf 1.15 1/13/00 Ian Formanek I18N * 15 Gandalf 1.14 1/9/00 Ian Formanek Removed debug printlns * 14 Gandalf 1.13 1/9/00 Ian Formanek Improved saving of * shortcuts * 13 Gandalf 1.12 1/9/00 Ian Formanek Improved loading and * adding shortcuts * 12 Gandalf 1.11 1/4/00 Ian Formanek * 11 Gandalf 1.10 1/4/00 Ian Formanek * 10 Gandalf 1.9 1/2/00 Ian Formanek Form serialized in newer * format * 9 Gandalf 1.8 12/21/99 Ian Formanek * 8 Gandalf 1.7 12/21/99 Ian Formanek Optimized adding * multiple <action, shortcut> pairs into Keymap * 7 Gandalf 1.6 12/1/99 Ian Formanek * 6 Gandalf 1.5 11/30/99 Ian Formanek * 5 Gandalf 1.4 11/30/99 Ian Formanek * 4 Gandalf 1.3 11/29/99 Ian Formanek * 3 Gandalf 1.2 11/26/99 Patrik Knakal * 2 Gandalf 1.1 11/25/99 Ian Formanek * 1 Gandalf 1.0 11/25/99 Ian Formanek * $ */